import gov.nih.mipav.model.structures.ModelImage;
import gov.nih.mipav.model.structures.VOI;
import gov.nih.mipav.model.structures.VOIVector;
import gov.nih.mipav.view.ViewUserInterface;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;

/**
 * Class intended to manage all the data related to the initial point factory.
 * 
 * @author marco.zanger
 */
public class InitialPointsFactory extends Module implements
    MethodRelated {

  /** The combo box containing the images to be used as initialPoints */
  private DefaultComboBoxModel initialPointsModel;

  /** The default setting to enable the use of the VOI provided by mipav */
  private String voiElement = "<VOI>";

  /** All the VOIs registered in the selected images */
  private HashMap<VOI, String> registeredVOIs;

  /**
   * The constructor. Lovely.
   */
  public InitialPointsFactory() {
    super();
    initialPointsModel = new DefaultComboBoxModel();
  }

  /**
   * The update method required by the interface. This will update the possible
   * choices to pick from.
   */
  protected void updateMe() {
    ViewUserInterface userInterface = ViewUserInterface.getReference();

    Enumeration<String> names = userInterface.getRegisteredImageNames();
    String selectedIP = (String) initialPointsModel.getSelectedItem();
    initialPointsModel.removeAllElements();
    initialPointsModel.addElement(voiElement);
    while (names.hasMoreElements()) {
      String temp = names.nextElement();
      initialPointsModel.addElement(temp);
    }

    if (initialPointsModel.getIndexOf(selectedIP) >= 0) {
      initialPointsModel.setSelectedItem(selectedIP);
    }
  }

  public Object getSelectedItem() {
    return initialPointsModel.getSelectedItem();
  }

  public JComboBox createComboBox() {
    JComboBox comboBox = new JComboBox(initialPointsModel);
    return comboBox;
  }

  private short[] getInitialPoints() {
    ViewUserInterface userInterface = ViewUserInterface.getReference();

    int height = JSMethodProxy.getInstance().getHeight();
    int width = JSMethodProxy.getInstance().getWidth();
    int depth = JSMethodProxy.getInstance().getDepth();

    short[] initialPoints = null;
    if (initialPointsModel.getSelectedItem() == voiElement) {

      initialPoints = getInitialPointsFromVOI(height, width, depth);
    } else {
      ModelImage ipImage = userInterface
          .getRegisteredImageByName((String) this.getSelectedItem());
      try {
        initialPoints = new short[height * width * depth];
        ipImage.exportData(0, height * width * depth, initialPoints);
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    return initialPoints;
  }

  private short[] getInitialPointsFromVOI(final int height, final int width,
      final int depth) {

    short[] initialPoints = new short[height * width * depth];
    for (int i = 0; i < initialPoints.length; i++) {
      initialPoints[i] = 0;
    }

    int[] xBounds = new int[2];
    int[] yBounds = new int[2];
    int[] zBounds = new int[2];

    RegionManager regionManager = RegionManager.getInstance();

    Collection<VOI> vois = registeredVOIs.keySet();
    for (Iterator<VOI> iterator = vois.iterator(); iterator.hasNext();) {
      VOI voi = (VOI) iterator.next();

      voi.getBounds(xBounds, yBounds, zBounds);

      for (int x = xBounds[0]; x <= xBounds[1]; x++) {
        for (int y = yBounds[0]; y <= yBounds[1]; y++) {
          for (int z = zBounds[0]; z <= zBounds[1]; z++) {

            if (this.inVOI(voi, x, y, z)
                && initialPoints[y + x * width + z * width * height] == 0) {

              // Y AND X MUST BE PASSED IN DIFFERENT ORDER TO
              // MATCH ORIGINAL IMAGES
              initialPoints[x + y * width + z * width * height] = (short) (regionManager
                  .getVOIValue(voi.getName()) + 1);
            }
          }
        }
      }
    }
    return initialPoints;
  }

  /**
   * Obtains all the VOI for all loaded images.
   */
  private void loadVOIs() {
    if (registeredVOIs == null)
      registeredVOIs = new HashMap<VOI, String>();
    registeredVOIs.clear();

    ViewUserInterface userInterface = ViewUserInterface.getReference();

    ArrayList<String> images = DataManager.getInstance().getImageManager()
        .getImagesNames();

    for (Iterator<String> iterator = images.iterator(); iterator.hasNext();) {
      String imageName = (String) iterator.next();
      this.getVOIfromImage(userInterface.getRegisteredImageByName(imageName));
    }
  }

  public HashMap<VOI, String> getRegisteredVOIs() {
    return registeredVOIs;
  }

  /**
   * Get all the VOI of given modelImage.
   * 
   * @param modelImage
   */
  private void getVOIfromImage(final ModelImage modelImage) {
    VOIVector vector = modelImage.getVOIs();
    for (Iterator<VOI> iterator = vector.iterator(); iterator.hasNext();) {
      VOI voi = (VOI) iterator.next();
      if (!registeredVOIs.containsKey(voi)) {
        registeredVOIs.put(voi, modelImage.getImageName());
      }
    }
  }

  /**
   * Updates all the data in the Proxy for the different methods to use.
   */
  public void updateProxy() {
    JSMethodProxy jsProxy = JSMethodProxy.getInstance();

    this.loadVOIs();
    RegionManager.getInstance().updateProxy();

    short[] initialPoints = this.getInitialPoints();

    jsProxy.setInitialPoints(initialPoints);
  }

  /**
   * Checks if the current loaded images are the same to the new ones. Since the
   * loaded images have the VOI element first we check its length is equal to
   * the registeredImageNames plus one, if it is we have to check all names are
   * the same.
   */
  /**
   * Only needs to be update in case the loaded images changed
   */
  @Override
  public boolean needsToBeUpdated() {
    boolean update = false;

    ViewUserInterface userInterface = ViewUserInterface.getReference();
    Enumeration<String> currentNames = userInterface.getRegisteredImageNames();

    int currentNamesLength = 1;
    while (currentNames.hasMoreElements()) {
      currentNamesLength++;
      String temp = currentNames.nextElement();
      update = update || (initialPointsModel.getIndexOf(temp) == -1);
    }

    update = update || (initialPointsModel.getSize() != currentNamesLength);
    return update;
  }
  
  public boolean inVOI(VOI voi,int x, int y, int z){
    boolean ret = false;
    float distance = voi.pointToContour(x, y, z);
    if (distance < 0.0)
      ret = true;
    return ret;
  }

}
